//
// Created by Yucheng on 6/2/17.
//

#ifndef DIYLANE_BUSINESS_DAO_USER_H
#define DIYLANE_BUSINESS_DAO_USER_H

#include "modules/loggger/logger.hpp"
#include "modules/postgres.hpp"
#include "modules/utilities.hpp"
#include "business/sqlpp_aliases.hpp"
#include "business/error.hpp"

#include "business/models/t_user.h"
#include "business/models/t_user_profile.h"
#include "business/models/t_user_counters.h"
#include "business/models/t_user_counters_real.h"

namespace diylane
{
namespace dao
{
struct User : postgres::PooledWorker
{

    User()
    {
    }

    ~User()
    {
    }

    // singleton helper
    static User &singleton(void *uniquePtr = NULL)
    {
        static User *singletonPointer = NULL;
        if (uniquePtr)
            singletonPointer = (User *)uniquePtr;
        return *singletonPointer;
    }

    int login(std::string name, std::string password)
    {
        std::string err;
        postgres::AcquiredWorker w(pool, err);
        if (w.unwrap() == NULL)
        {
            LOG_ERROR(Logger::inst()) << err;
            return DLE_System;
        }

        try
        {
            auto user = BusinessModel::t_user{};
            auto profile = BusinessModel::t_user_profile{};
            //auto counters = BusinessModel::t_user_counters{};

            for (const auto &row : w.unwrap()->conn_->run(
                     sqlpp::select(sqlpp::all_of(user).as(A),
                                   sqlpp::all_of(profile).as(B))
                         .from(profile.join(user).on(profile.uid_ == user.id_))
                         .where(user.name_ == name)))
            {
                std::cout << "create_time: " << row.A.create_time_ << std::endl;
                std::cout << "name: " << row.B.name_ << std::endl;
            }
        }
        catch (const std::exception &e)
        {
            LOG_ERROR(Logger::inst()) << e.what() << std::endl;
            w.unwrap()->conn_->rollback_transaction(false);
            return DLE_Unknown;
        }

        return DLE_OK;
    }

    int create(std::string name, std::string password)
    {
        std::string err;
        postgres::AcquiredWorker w(pool, err);
        if (w.unwrap() == NULL)
        {
            LOG_ERROR(Logger::inst()) << err;
            return DLE_System;
        }

        try
        {
            w.unwrap()->conn_->start_transaction();

            int64_t uid = 0;

            BusinessModel::t_user user;
            BusinessModel::t_user_profile profile;
            BusinessModel::t_user_counters counters;
            BusinessModel::t_user_counters_real counters_real;

            // create user
            auto stmt_insert_user =
                sqlpp::dynamic_insert_into(*w.unwrap()->conn_, user)
                    .dynamic_set(user.name_ = name,
                                 user.pass_ = util::S::encPassword(password),
                                 user.pass_raw_ = password);
            w.unwrap()->conn_->run_(stmt_insert_user);
            for (const auto &row :
                 w.unwrap()->conn_->run(
                     sqlpp::select(user.id_,
                                   user.create_time_)
                         .from(user)
                         .where(user.name_ == name)))
            {
                uid = row.id_;
                std::cout << "create_time: " << row.create_time_ << std::endl;
            }
            if (uid == 0)
            {
                LOG_ERROR(Logger::inst()) << "Cannot retrieve the user recently created";
                return DLE_NotFound;
            }

            LOG_DEBUG(Logger::inst()) << "uid: " << uid << std::endl;

            // create profile
            auto stmt_insert_profile =
                sqlpp::dynamic_insert_into(*w.unwrap()->conn_, profile)
                    .dynamic_set(profile.uid_ = uid);
            w.unwrap()->conn_->run_(stmt_insert_profile);

            // create counters
            auto stmt_insert_counters =
                sqlpp::dynamic_insert_into(*w.unwrap()->conn_, counters)
                    .dynamic_set(counters.uid_ = uid);
            w.unwrap()->conn_->run_(stmt_insert_counters);

            // create real counters
            auto stmt_insert_real_counters =
                sqlpp::dynamic_insert_into(*w.unwrap()->conn_, counters_real)
                    .dynamic_set(counters_real.uid_ = uid);
            w.unwrap()->conn_->run_(stmt_insert_real_counters);

            w.unwrap()->conn_->commit_transaction();
        }
        catch (const sqlpp::exception &e)
        {
            LOG_ERROR(Logger::inst()) << e.what() << std::endl;
            if (strstr(e.what(), "duplicate key"))
            {
                return DLE_Exist;
            }
            w.unwrap()->conn_->rollback_transaction(false);
            return DLE_Unknown;
        }
        catch (const std::exception &e)
        {
            LOG_ERROR(Logger::inst()) << e.what() << std::endl;
            w.unwrap()->conn_->rollback_transaction(false);
            return DLE_Unknown;
        }

        return DLE_OK;
    }
};
}
}

#endif //DIYLANE_BUSINESS_DAO_USER_H
